#!/usr/bin/env python3

import pandas as pd
import dash
import dash_bootstrap_components as dbc

from .utils import db_opt as do
from . import styles
from dash import dcc

dash.register_page(__name__, path_template="/Adminstrator/<admin_id>")

course_cache: dict[str, list[dict]] = {}
courses = do.DatabaseOperation().get_courses()
students = do.DatabaseOperation().get_students()

course_sidebar = dash.html.Div(
  [
    dash.html.H2("Course list", className="display-3"),
    dash.html.Hr(),
    dash.html.P("These are all the courses in this term", className="lead"),
    dbc.ListGroup(
      [
        dbc.ListGroupItem(
          course.name,
          id={"type": "c_name", "name": course.name, "cid": course.id}, action=True, active=False
        ) for course in courses
      ],
      id="course_list"
    ),
    dash.html.Hr(),
    dbc.Button("Extra(greater-table)", color="success", id="trigger_greater"),
  ],
  style=styles.sidebar_style
)

operation_form = dash.html.Div(
  dbc.Form([
    dbc.Row(
      [
        dbc.Label("Operate on:", width="auto"),
        dbc.Col(
          dbc.Input(id="input_student", type="username", placeholder="Search for the name of a Student within this course"),
        ),
        dbc.Col(dbc.Button("Search", color="primary", id="submit_search"), width="auto")
      ], className="g-4"
    ),
    dash.html.Hr(),
    dash.html.Div(
      dbc.Button("Or just add a student into this course", color="danger", id="trigger_show_list"),
      className="d-grid gap-2"
    )
  ])
)

course_content = dash.html.Div(
  id="course_content",
  style=styles.sidebar_page_content_style
)

layout = dbc.Container([
    dcc.Store(id="cache_data", data={}),
    course_sidebar,
    course_content,
    dbc.Modal(id="greater_modal", is_open=False, centered=True, size="lg"),
])
def get_parm_list(param, listof_dict):
  return [i[param] for i in listof_dict]

def generate_course_page(course_id, course_name) -> dash.html.Div:
  do_inst = do.DatabaseOperation()
  stu_info = do_inst.get_student_score_in_course(course_id)

  course_cache[course_id] = stu_info
  df = pd.DataFrame({
      "id":    get_parm_list("id",    stu_info),
      "name":  get_parm_list("name",  stu_info),
      "sex":   get_parm_list("sex",   stu_info),
      "score": get_parm_list("score", stu_info)
  })
  return dash.html.Div([
    dash.html.H1(course_name, style={'textAlign': 'center'}),
    dbc.Table.from_dataframe(df, striped=True, bordered=True, hover=True),
    dash.html.Hr(),
    operation_form,
    dbc.Modal(id="student_modal", is_open=False, centered=True),
    dbc.Modal(id="flag_modal", is_open=False, centered=True),
  ])

def create_operation_modal(sname, course):
  '''
  Create a popup window that shows the student's score in this course, if admin
  selected alter in Operation(), just show a input box for target value
  '''
  data, = [i for i in course_cache[course["id"]] if i["name"] == sname]
  if data is None:
    raise ValueError("The student is not in the course")

  return dbc.Container(
    [
      dash.html.Div([
        dash.html.Img(
          src=dash.get_asset_url("student_img.png"),
          width=250,
          className="rounded-circle mx-auto d-flex img-thumbnail"
        ),
        dash.html.Div(
          [dbc.Badge("{}: {}".format(k, v), className="ms-1", color="success") for k, v in data.items()]
        )
      ]),
      dash.html.Hr(),
      dbc.Alert("Now select the operation you want to apply.", color="warning"),
      dbc.Row([
        dbc.Col(
          dbc.RadioItems(
            [
              {"label": "Alter", "value": 1},
              {"label": "Delete", "value": 2}
            ],
            id="opt_select", inline=True
          ),
        ),
        dbc.Col(dbc.Button("Submit", id="submit_operation", className="me-2"), width="auto"),
      ]),
    ],
    style={"padding": "20px"}
  )

def create_flag_modal(valid):
  if valid:
    return dash.html.Div(
      dbc.Alert("Operation succeed!", color="success"),
      style={"padding": "10px"}
    )
  return dash.html.Div(dbc.Alert("Failed! please make sure your operation is valid!", color="danger"),
                       style={"padding": "10px"})

def create_insert_modal():
  return dbc.Container(
    [
      dbc.Accordion([
        dbc.AccordionItem(
          dbc.Button("Add this student", id={"type": "add_button", "id": st["id"]}),
          title="{}-{}".format(st["id"], st["name"])
        )
        for st in students
      ])
    ],
    style={"padding": "20px"}
  )

@dash.callback([dash.Output("cache_data", "data"), dash.Output("course_content", "children")],
               [dash.Input({"type": "c_name", "name": dash.ALL, "cid": dash.ALL}, "n_clicks"),
                dash.State("cache_data", "data")],
               prevent_initial_call=True)
def update_course_content(_, data):
  data["id"] = dash.ctx.triggered_id.cid
  data["name"] = dash.ctx.triggered_id.name
  return data, generate_course_page(dash.ctx.triggered_id.cid,
                                    dash.ctx.triggered_id.name)

@dash.callback([dash.Output("student_modal", "children"),
                dash.Output("student_modal", "is_open"),
                dash.Output("input_student", "invalid")],
               dash.Input("submit_search", "n_clicks"),
               [dash.State("input_student", "value"),
                dash.State("cache_data", "data")],
               prevent_initial_call=True)
def searching(_, sname, course):
  try:
    return create_operation_modal(sname, course), True, False
  except Exception as err:
    return dash.no_update, False, True

@dash.callback(
  dash.Output("student_modal", "children", allow_duplicate=True),
  dash.Input("submit_operation", "n_clicks"),
  [dash.State("opt_select", "value"), dash.State("input_student", "value"),
   dash.State("cache_data", "data")],
  prevent_initial_call=True,
)
def create_comfirm_modal(_, opt, sname, course):
  match opt:
    case 1:
      data, = [i for i in course_cache[course["id"]] if i["name"] == sname]
      return dash.html.Div(
        [
          dash.html.P("Current student's score is {}, Enter the score you want to change it to.".format(data["score"])),
          dash.html.Hr(),
          dbc.Form(
            dbc.Row([
              dbc.Label("Score:", width="auto"),
              dbc.Col(dbc.Input(id="alter_input", type="number", placeholder="Enter score", min=0, max=100),
                      className="me-3"),
              dbc.Col(dbc.Button("Submit", color="primary", id="submit_alter"), width="auto")
            ])
          ),
        ],
        style={"padding": "20px"}
      )
    case 2:
      return dash.html.Div(
        [
          dash.html.H1("Confirm"),
          dash.html.Hr(),
          dbc.Row([
            dash.html.P("Really to delete this student from this course?"),
            dbc.Button("Just delete", id="delete_submit", color="danger", className="me-2"),
          ])
        ],
        style={"padding": "20px"}
      )
  raise dash.exceptions.PreventUpdate

@dash.callback(dash.Output("student_modal", "children", allow_duplicate=True),
               dash.Input("delete_submit", "n_clicks"),
               [dash.State("input_student", "value"),dash.State("cache_data", "data")],
               prevent_initial_call=True)
def handle_student_delete(clicked, sname, course):
  if clicked is None:
    raise dash.exceptions.PreventUpdate

  data, = [i for i in course_cache[course["id"]] if i["name"] == sname]
  return create_flag_modal(
    do.DatabaseOperation().del_stu_from_course(data["id"], course["id"])
  )

@dash.callback(dash.Output("student_modal", "children", allow_duplicate=True),
               dash.Input("submit_alter", "n_clicks"),
               dash.State("alter_input", "value"),
               dash.State("input_student", "value"),
               dash.State("cache_data", "data"),
               prevent_initial_call=True)
def handle_student_alter(clicked, score, sname, course):
  if clicked is None:
    raise dash.exceptions.PreventUpdate

  data, = [i for i in course_cache[course["id"]] if i["name"] == sname]
  return create_flag_modal(
    do.DatabaseOperation().alter_score_for_course(data["id"], score, course["id"])
  )

@dash.callback(
  [dash.Output("student_modal", "children", allow_duplicate=True),
   dash.Output("student_modal", "is_open", allow_duplicate=True)],
  dash.Input("trigger_show_list", "n_clicks"),
  prevent_initial_call=True
)
def show_student_list(n_clicks):
  if n_clicks is None:
    raise dash.exceptions.PreventUpdate
  return create_insert_modal(), True

@dash.callback(
  [dash.Output("student_modal", "children", allow_duplicate=True),
   dash.Output("cache_data", "data", allow_duplicate=True)],
  dash.Input({"type": "add_button", "id": dash.ALL}, "n_clicks"),
  dash.State("cache_data", "data"),
  prevent_initial_call=True
)
def show_insert_after_selection(n_clicks, cache):
  if n_clicks is None or all(i is None for i in n_clicks):
    raise dash.exceptions.PreventUpdate

  cache["add_student"] = dash.ctx.triggered_id.id
  return dash.html.Div(
    dbc.Form([
      dbc.Label("Add a score for this student:", width="auto"),
      dbc.Col(dbc.Input(id="add_score_input", type="number", placeholder="Enter score", min=0, max=100),
              className="me-3"),
      dbc.Col(dbc.Button("Commit", color="success", id="add_score_button"), width="auto")
    ]),
    style={"padding": "20px"}
  ), cache

@dash.callback(
  [dash.Output("flag_modal", "children"),
   dash.Output("flag_modal", "is_open"),
   dash.Output("student_modal", "is_open", allow_duplicate=True)],
  dash.Input("add_score_button", "n_clicks"),
  [dash.State("add_score_input", "value"),
   dash.State("cache_data", "data")],
  prevent_initial_call=True
)
def add_student_for_course(clicked, sc, cache):
  if clicked is None:
    raise dash.exceptions.PreventUpdate
  return [create_flag_modal(do.DatabaseOperation().add_student_into_course(cache["add_student"],
                                                                           cache["id"], sc)),
          True, False]

@dash.callback(
  dash.Output("greater_modal", "children"),
  dash.Output("greater_modal", "is_open"),
  dash.Input("trigger_greater", "n_clicks")
)
def greater_table_generation(n_clicks):
  if n_clicks is None:
    raise dash.exceptions.PreventUpdate

  items = do.DatabaseOperation().get_all_greater()

  df = pd.DataFrame({key: get_parm_list(key, items) for key in items[0].keys()})

  return dash.html.Div([
    dbc.Alert("Here's a list of students whose course grades are greater than the average grade for "
              "their chosen course. :)",
              color="warning"),
    dbc.Table.from_dataframe(df, striped=True, bordered=True, hover=True),
  ]), True
